package com.cloudfun.campusshare.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.cloudfun.campusshare.common.constant.*;
import com.cloudfun.campusshare.common.exception.BusinessException;
import com.cloudfun.campusshare.common.model.LiquidPumpVo;
import com.cloudfun.campusshare.common.model.dto.*;
import com.cloudfun.campusshare.common.model.vo.*;
import com.cloudfun.campusshare.entity.*;
import com.cloudfun.campusshare.repo.*;
import com.cloudfun.campusshare.service.DeviceMetaModeService;
import com.cloudfun.campusshare.service.DeviceModeService;
import com.cloudfun.campusshare.service.ReplenishService;
import com.cloudfun.campusshare.util.AmountUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by Huqin on 2020/4/20
 */
@Slf4j
@Service
public class DeviceModeServiceImpl implements DeviceModeService {

    @Autowired
    private DeviceModeRepo deviceModeRepo;

    @Autowired
    private DeviceEntityRepo deviceEntityRepo;

    @Autowired
    private DeviceMetaModeService deviceMetaModeService;

    @Autowired
    private DeviceProgramRepo deviceProgramRepo;

    @Override
    @Transactional
    public void save(String modeSetId, List<DeviceModeEntity> modeEntities) {
        Set<String> metaModeIds = modeEntities.stream().map(DeviceModeEntity::getMetaModeId).collect(Collectors.toSet());
        List<DeviceModeEntity> toBeDeleteList = new ArrayList<>();
        Map<String, DeviceModeEntity> metaModeIdsInDB = new HashMap<>();
        Map<String, DeviceModeEntity> idModeEntityMapInDB = deviceModeRepo.findByModeSetId(modeSetId).stream()
                .peek(entity -> {
                    metaModeIdsInDB.put(entity.getMetaModeId(), entity);
                    if (!metaModeIds.contains(entity.getMetaModeId())) {
                        toBeDeleteList.add(entity);
                    }
                })
                .collect(Collectors.toMap(DeviceModeEntity::getId, Function.identity()));
        if (CollectionUtils.isNotEmpty(toBeDeleteList)) {
            deviceModeRepo.deleteAll(toBeDeleteList);
        }
        // 新建
        Set<String> allMetaModeIds = deviceMetaModeService.getDeviceMetaModeMap().keySet();
        List<DeviceModeEntity> deviceModeEntities = modeEntities.stream()
                .filter(entity -> allMetaModeIds.contains(entity.getMetaModeId()))
                .map(modeEntity -> {
                    DeviceModeEntity modeEntityInDB;
                    if (StringUtils.isNotBlank(modeEntity.getId())) {
                        modeEntityInDB = idModeEntityMapInDB.get(modeEntity.getId());
                        if (null == modeEntityInDB) {
                            throw new BusinessException(Code.NODATA, "模式集不存在");
                        }
                        return modeEntityInDB;
                    } else {
                        if (metaModeIdsInDB.containsKey(modeEntity.getMetaModeId())) {
                            modeEntityInDB = metaModeIdsInDB.get(modeEntity.getMetaModeId());
                        } else {
                            modeEntityInDB = new DeviceModeEntity();
                        }
                    }
                    BeanUtil.copyProperties(modeEntity, modeEntityInDB, CopyOptions.create().setIgnoreNullValue(true));
                    return modeEntityInDB;
                }).collect(Collectors.toList());
        deviceModeRepo.saveAll(deviceModeEntities);
    }

    @Override
    public List<DeviceModeDTO> getMerchantModeVOs(String merchantId) {
        if (StringUtils.isBlank(merchantId)) {
            return new ArrayList<>();
        }
        List<DeviceModeEntity> deviceModeEntities = deviceModeRepo.findByMerchantId(merchantId);
        return this.convertToDeviceModeDTO(deviceModeEntities, null);
    }


    @Override
    public DeviceModeDTO getMerchantMode(String modeId) {
        DeviceModeEntity modeEntity = deviceModeRepo.findById(modeId).orElseThrow(() -> new BusinessException(Code.NODATA, "模式不存在"));
        return this.convertToDeviceModeDTO(modeEntity, deviceMetaModeService.getDeviceMetaModeMap());
    }

    @Override
    public List<DeviceModeVO> getMerchantModeVOs(String merchantId, String modeSetId, String deviceType) {
        if (StringUtils.isBlank(merchantId) || StringUtils.isBlank(deviceType)) {
            return new ArrayList<>();
        }
        List<DeviceModeEntity> deviceModeEntities;
        if (StringUtils.isNotBlank(modeSetId)) {
            deviceModeEntities = deviceModeRepo.findByMerchantIdAndModeSetId(merchantId, modeSetId);
        } else {
            deviceModeEntities = deviceModeRepo.findByMerchantId(merchantId);
        }
        return this.convertToDeviceModeDTO(deviceModeEntities, deviceType).stream()
                .map(modeDTO -> this.toModeVO(deviceType, modeDTO)).collect(Collectors.toList());
    }

    private DeviceModeVO toModeVO(String deviceType, DeviceModeDTO deviceModeDTO) {
        DeviceMetaModeDTO metaMode = deviceModeDTO.getMetaMode();
        List<DeviceProgramDTO> programs = metaMode.getPrograms();
        Map<String, String> additionPrograms = programs.stream().collect(Collectors.toMap(DeviceProgramDTO::getName, DeviceProgramDTO::getValue));
        Integer time = deviceModeDTO.getMetaMode().getTime();
        switch (deviceType) {
            case BaseConstant.GXY8_NAME:
            case BaseConstant.GXY13_5_NAME:
                // 滚筒洗衣
                String washType = additionPrograms.get("washType");
                boolean heat40 = StringUtils.equals(additionPrograms.get("heat"), "heat40");
                boolean heat60 = StringUtils.equals(additionPrograms.get("heat"), "heat60");
                boolean speedUpStatus = Boolean.parseBoolean(additionPrograms.get("speedUpStatus"));
                boolean quietRinseStatus = Boolean.parseBoolean(additionPrograms.get("quietRinseStatus"));
                boolean addLaundryDetergent = Boolean.parseBoolean(additionPrograms.get("addLaundryDetergent"));
                RollerWasherModeVO rollerWasherModeVO = new RollerWasherModeVO();
                HeatStatus heatStatus;
                if (heat40 || heat60) {
                    heatStatus = heat40 ? HeatStatus.HEAT_40 : HeatStatus.HEAT_60;
                } else {
                    heatStatus = HeatStatus.HEAT_OFF;
                }
                rollerWasherModeVO.setWashType(washType);
                rollerWasherModeVO.setHeatStatus(heatStatus);
                rollerWasherModeVO.setSpeedUpStatus(speedUpStatus);
                rollerWasherModeVO.setQuietRinseStatus(quietRinseStatus);
                rollerWasherModeVO.setAddLaundryDetergent(addLaundryDetergent);
                BeanUtil.copyProperties(deviceModeDTO, rollerWasherModeVO);
                rollerWasherModeVO.setTime(time);
                rollerWasherModeVO.setDeviceType(deviceType);
                return rollerWasherModeVO;
            case BaseConstant.GGY8_NAME:
            case BaseConstant.GGY13_5_NAME:
                // 滚筒干衣
                DryerModeVO dryerModeVO = new DryerModeVO();
                DryerOperateVO dryerOperateVO = deviceMetaModeService.metaModeToDryerOperateVO(metaMode);
                BeanUtil.copyProperties(dryerOperateVO, dryerModeVO);
                BeanUtil.copyProperties(deviceModeDTO, dryerModeVO);
                dryerModeVO.setTime(time);
                dryerModeVO.setDeviceType(deviceType);
                return dryerModeVO;
            case BaseConstant.BXY6_5_NAME:
            case BaseConstant.BXYD6_5_NAME:
                // 波轮洗衣
                PulseWasherModeVO pulseWasherModeVO = new PulseWasherModeVO();
                PulseWasherOperateVO pulseWasherOperateVO = deviceMetaModeService.metaModeToPulseWasherOperateVO(metaMode);
                BeanUtil.copyProperties(pulseWasherOperateVO, pulseWasherModeVO);
                BeanUtil.copyProperties(deviceModeDTO, pulseWasherModeVO);
                pulseWasherModeVO.setTime(time);
                pulseWasherModeVO.setDeviceType(deviceType);
                return pulseWasherModeVO;
            case BaseConstant.BXX6_5_NAME:
                // 洗鞋
                ShoeWasherModeVO shoeWasherModeVO = new ShoeWasherModeVO();
                ShoeWasherOperateVO shoeWasherOperateVO = deviceMetaModeService.metaModeToShoeWasherOperateVO(metaMode);
                BeanUtil.copyProperties(shoeWasherOperateVO, shoeWasherModeVO);
                BeanUtil.copyProperties(deviceModeDTO, shoeWasherModeVO);
                shoeWasherModeVO.setTime(time);
                shoeWasherModeVO.setDeviceType(deviceType);
                return shoeWasherModeVO;
        }
        return null;
    }

    private DeviceModeDTO convertToDeviceModeDTO(DeviceModeEntity deviceModeEntity, Map<String, DeviceMetaModeDTO> deviceMetaModeMap) {
        DeviceMetaModeDTO deviceMetaModeDTO = deviceMetaModeMap.get(deviceModeEntity.getMetaModeId());
        if (null == deviceMetaModeDTO) {
            return null;
        }
        DeviceModeDTO deviceModeDTO = new DeviceModeDTO();
        BeanUtil.copyProperties(deviceModeEntity, deviceModeDTO, "price");
        deviceModeDTO.setMetaMode(deviceMetaModeDTO);
        deviceModeDTO.setPrice(AmountUtil.amountRoundStr(deviceModeEntity.getPrice(), 2));
        return deviceModeDTO;
    }

    private List<DeviceModeDTO> convertToDeviceModeDTO(List<DeviceModeEntity> deviceModeEntities, String deviceType) {
        Map<String, DeviceMetaModeDTO> deviceMetaModeMap = StringUtils.isBlank(deviceType) ? deviceMetaModeService.getDeviceMetaModeMap()
                : deviceMetaModeService.getDeviceMetaModeMap(deviceType);
        return deviceModeEntities.stream()
                .map(deviceModeEntity -> this.convertToDeviceModeDTO(deviceModeEntity, deviceMetaModeMap))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    @Autowired
    private PumpWashRelRepo pumpWashRelRepo;
    @Autowired
    private DeviceModeSetRepo modeSetRepo;

    @Autowired
    private DeviceMetaModeRepo metaModeRepo;

    @Autowired
    private ReplenishService replenishService;

    @Override
    public List<DeviceModeVO> getMerchantModeVOsByMachineNo(String machineNo) {
        DeviceEntity deviceEntity = deviceEntityRepo.findByMachineNo(machineNo);
        if (deviceEntity == null) {
            throw new BusinessException(Code.NODATA, "设备不存在");
        }
        if (StringUtils.isBlank(deviceEntity.getUserId())) {
            throw new BusinessException(Code.DEVICE_NOT_BOUND);
        }
        if (StringUtils.isBlank(deviceEntity.getModeSetId())) {
            return new ArrayList<>();
        }
        List<DeviceModeVO> voList = this.getMerchantModeVOs(deviceEntity.getUserId(), deviceEntity.getModeSetId(), deviceEntity.getDeviceType());
        PumpWashRel byWashId = pumpWashRelRepo.findByWashImei(deviceEntity.getImei());
        if (byWashId == null || !byWashId.isBindStatus()) {
            return voList;
        }
        DeviceEntity liquidPumpDevice = deviceEntityRepo.findByImei(byWashId.getPumpImei());
        ReplenishDTO replenishInfo = replenishService.getReplenishInfo(byWashId.getPumpImei());
        if (liquidPumpDevice != null) {
            List<DeviceModeEntity> modeEntities = deviceModeRepo.findByModeSetId(liquidPumpDevice.getModeSetId());
            List<LiquidPumpVo> liquidPumpVos = modeEntities.stream().map(deviceModeEntity -> {
                Optional<DeviceMetaModeEntity> metaModeEntity = metaModeRepo.findById(deviceModeEntity.getMetaModeId());
                LiquidPumpVo vo = new LiquidPumpVo();
                if (deviceModeEntity.getMetaModeId().equals("804") || deviceModeEntity.getMetaModeId().equals("803")) {
                    vo.setStatus(replenishInfo.isLaundryOperation());
                } else {
                    vo.setStatus(replenishInfo.isDisinfectantOperation());
                }
                vo.setId(deviceModeEntity.getId());
                vo.setPrice(AmountUtil.amountRoundStr(deviceModeEntity.getPrice(), 2));
                vo.setName(metaModeEntity.get().getName());
                return vo;
            }).filter(Objects::nonNull).collect(Collectors.toList());
            voList.stream().forEach(deviceModeVO -> {
                if (deviceModeVO instanceof PulseWasherModeVO) {
                    if (((PulseWasherModeVO) deviceModeVO).getWashType() != 4 && (deviceModeVO.getDeviceType().contains("XX") || deviceModeVO.getDeviceType().contains("XY"))) {
                        ((PulseWasherModeVO) deviceModeVO).setLiquidPump(liquidPumpVos);
                    }
                } else if (deviceModeVO instanceof RollerWasherModeVO) {
                    if (!((RollerWasherModeVO) deviceModeVO).getWashType().equals("4") && (deviceModeVO.getDeviceType().contains("XX") || deviceModeVO.getDeviceType().contains("XY"))) {
                        ((RollerWasherModeVO) deviceModeVO).setLiquidPump(liquidPumpVos);
                    }
                }

            });
        }
        return voList;
    }

    @Override
    public List<DeviceModeSimpleDTO> getModesByModeSetId(String modeSetId) {
        if (StringUtils.isBlank(modeSetId)) {
            return new ArrayList<>();
        }
        List<DeviceModeEntity> deviceModeEntities = deviceModeRepo.findByModeSetId(modeSetId);
        return this.toSimpleDTO(deviceModeEntities);
    }

    @Override
    public List<DeviceModeSimpleDTO> getModesByModeSetIds(Collection<String> modeSetIds) {
        if (CollectionUtils.isEmpty(modeSetIds)) {
            return new ArrayList<>();
        }
        List<DeviceModeEntity> deviceModeEntities = deviceModeRepo.findByModeSetIdIn(modeSetIds);
        return this.toSimpleDTO(deviceModeEntities);
    }

    private List<DeviceModeSimpleDTO> toSimpleDTO(List<DeviceModeEntity> deviceModeEntities) {
        List<DeviceModeDTO> deviceModeDTOS = this.convertToDeviceModeDTO(deviceModeEntities, null);
        List<DeviceModeSimpleDTO> res = deviceModeDTOS.stream().map(deviceModeDTO -> {
            DeviceModeSimpleDTO deviceModeSimpleDTO = new DeviceModeSimpleDTO();
            BeanUtil.copyProperties(deviceModeDTO, deviceModeSimpleDTO);
            DeviceMetaModeDTO metaMode = deviceModeDTO.getMetaMode();
            deviceModeSimpleDTO.setMetaModeId(metaMode.getId());
            deviceModeSimpleDTO.setName(metaMode.getName());
            deviceModeSimpleDTO.setTime(metaMode.getTime());
            return deviceModeSimpleDTO;
        }).collect(Collectors.toList());
        return res;
    }

    @Override
    public void deleteByModeSetId(String modeSetId) {
        if (StringUtils.isBlank(modeSetId)) {
            return;
        }
        List<DeviceModeEntity> deviceModeEntities = deviceModeRepo.findByModeSetId(modeSetId);
        deviceModeRepo.deleteAll(deviceModeEntities);
    }

}
